﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using PSE.Framework.Configuration;
using PSE.Framework.ErrorLogging.Configuration;
using System.IO;
using System.Timers;
using System.Xml.Linq;
using System.Xml.XPath;

namespace PSE.Framework.ErrorLogging
{

    /// <summary>
    /// Classe para gravação de logs de erro
    /// </summary>
    public static class ErrorLogger
    {
        #region Constantes
        private const string EVENT_VIEWER_LOG_NAME = "BMFBOVESPA";
        private const string EVENT_VIEWER_DEFAULT_SYSTEM = "BM&FBOVESPA";
        private const int EVENT_VIEWER_MAXIMUM_KILOBYTES = 102400; //com base em 64
        private const int EVENT_VIEWER_RETENTION_DAYS = 7;
        #endregion

        #region Métodos que recebem Type e Exception
        /// <summary>
        /// Grava uma mensagem no log de erros, utilizando o Sistema e o Componente correntes
        /// </summary>
        /// <param name="type">Tipo do erro</param>
        /// <param name="ex">Exceção a ser gravada</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(LogType type, Exception ex)
        {
            return WriteLog(ApplicationConfiguration.CurrentSystem, ApplicationConfiguration.CurrentComponent, type, ex);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros, utilizando o Sistema e o Componente correntes
        /// </summary>
        /// <param name="type">Tipo do erro</param>
        /// <param name="ex">Exceção a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(LogType type, Exception ex, Dictionary<string, object> extensionData)
        {
            return WriteLog(ApplicationConfiguration.CurrentSystem, ApplicationConfiguration.CurrentComponent, type, ex, extensionData);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros, utilizando o Sistema e o Componente correntes
        /// </summary>
        /// <param name="type">Tipo do erro</param>
        /// <param name="ex">Exceção a ser gravada</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(LogType type, Exception ex, LogOutputType outputType)
        {
            return WriteLog(ApplicationConfiguration.CurrentSystem, ApplicationConfiguration.CurrentComponent, type, ex, outputType);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros, utilizando o Sistema e o Componente correntes
        /// </summary>
        /// <param name="type">Tipo do erro</param>
        /// <param name="ex">Exceção a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(LogType type, Exception ex, Dictionary<string, object> extensionData, LogOutputType outputType)
        {
            return WriteLog(ApplicationConfiguration.CurrentSystem, ApplicationConfiguration.CurrentComponent, type, ex, extensionData, outputType);
        }
        #endregion

        #region Métodos que recebem Type e Message
        /// <summary>
        /// Grava uma mensagem no log de erros, utilizando o Sistema e o Componente correntes
        /// </summary>
        /// <param name="type">Tipo do erro</param>
        /// <param name="message">Mensagem a ser gravada</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(LogType type, string message)
        {
            return WriteLog(ApplicationConfiguration.CurrentSystem, ApplicationConfiguration.CurrentComponent, type, message);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros, utilizando o Sistema e o Componente correntes
        /// </summary>
        /// <param name="type">Tipo do erro</param>
        /// <param name="message">Mensagem a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(LogType type, string message, Dictionary<string, object> extensionData)
        {
            return WriteLog(ApplicationConfiguration.CurrentSystem, ApplicationConfiguration.CurrentComponent, type, message, extensionData);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros, utilizando o Sistema e o Componente correntes
        /// </summary>
        /// <param name="type">Tipo do erro</param>
        /// <param name="message">Mensagem a ser gravada</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(LogType type, string message, LogOutputType outputType)
        {
            return WriteLog(ApplicationConfiguration.CurrentSystem, ApplicationConfiguration.CurrentComponent, type, message, outputType);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros, utilizando o Sistema e o Componente correntes
        /// </summary>
        /// <param name="type">Tipo do erro</param>
        /// <param name="message">Mensagem a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(LogType type, string message, Dictionary<string, object> extensionData, LogOutputType outputType)
        {
            return WriteLog(ApplicationConfiguration.CurrentSystem, ApplicationConfiguration.CurrentComponent, type, message, extensionData, outputType);
        }
        #endregion

        #region Métodos que recebem System, Source, Type e Exception
        /// <summary>
        /// Grava uma exceção no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="ex">Exceção a ser gravada</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, Exception ex)
        {
            return WriteLog(system, source, type, (object)ex, null, LogOutputType.All, null);
        }

        /// <summary>
        /// Grava uma exceção no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="ex">Exceção a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, Exception ex, Dictionary<string, object> extensionData)
        {
            return WriteLog(system, source, type, (object)ex, extensionData, LogOutputType.All, null);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="ex">Exceção a ser gravada</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, Exception ex, LogOutputType outputType)
        {
            return WriteLog(system, source, type, (object)ex, null, outputType, null);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="ex">Exceção a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, Exception ex, Dictionary<string, object> extensionData, LogOutputType outputType)
        {
            return WriteLog(system, source, type, (object)ex, extensionData, outputType, null);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="ex">Exceção a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <param name="config">Configurações de gravação</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, Exception ex, Dictionary<string, object> extensionData, LogOutputType outputType, ErrorLoggingConfigurationSection config)
        {
            return WriteLog(system, source, type, (object)ex, extensionData, outputType, config);
        }
        #endregion

        #region Métodos que recebem System, Source, Type e Message
        /// <summary>
        /// Grava uma mensagem no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="message">Mensagem a ser gravada</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, string message)
        {
            return WriteLog(system, source, type, (object)message, null, LogOutputType.All, null);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="message">Mensagem a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, string message, Dictionary<string, object> extensionData)
        {
            return WriteLog(system, source, type, (object)message, extensionData, LogOutputType.All, null);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="message">Mensagem a ser gravada</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, string message, LogOutputType outputType)
        {
            return WriteLog(system, source, type, (object)message, null, outputType, null);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="message">Mensagem a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, string message, Dictionary<string, object> extensionData, LogOutputType outputType)
        {
            return WriteLog(system, source, type, (object)message, extensionData, outputType, null);
        }

        /// <summary>
        /// Grava uma mensagem no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="message">Mensagem a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <param name="config">Configurações de gravação</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, string message, Dictionary<string, object> extensionData, LogOutputType outputType, ErrorLoggingConfigurationSection config)
        {
            return WriteLog(system, source, type, (object)message, extensionData, outputType, config);
        }
        #endregion

        #region Métodos que recebem LogData
        /// <summary>
        /// Grava uma mensagem no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="logData">Mensagem a ser gravada</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <param name="config">Configurações de gravação</param>
        /// <returns>Informações logadas</returns>
        public static LogData WriteLogData(SystemComponent system, SystemComponent source, LogType type, string logData, LogOutputType outputType, ErrorLoggingConfigurationSection config)
        {
            return WriteLog(system, source, type, (object)logData, new Dictionary<string, object>() { { "IsLogData", true } }, outputType, config);
        }
        #endregion

        #region WriteLog
        /// <summary>
        /// Grava uma mensagem no log de erros
        /// </summary>
        /// <param name="system">Sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="source">Componente do sistema que originou o erro, deve ser uma das constantes em <c>PSE.Framework.Configuration.SystemComponents</c></param>
        /// <param name="type">Tipo do erro</param>
        /// <param name="message">Mensagem a ser gravada</param>
        /// <param name="extensionData">Informações adicionais a serem gravadas</param>
        /// <param name="outputType">Local onde o log será gravado</param>
        /// <param name="config">Configurações de gravação</param>
        /// <returns>Informações logadas</returns>
        private static LogData WriteLog(SystemComponent system, SystemComponent source, LogType type, object message, Dictionary<string, object> extensionData, LogOutputType outputType, ErrorLoggingConfigurationSection config)
        {
            LogData logData = new LogData();
            ErrorLoggingConfigurationSection errorLoggingConfiguration = null;
            string logFilePath = null;
            bool isLogData = false;

            string systemName = !string.IsNullOrEmpty(system.Name) ? system.Name : string.Empty;
            string systemDescription = !string.IsNullOrEmpty(system.Description) ? system.Description : string.Empty;

            string sourceName = !string.IsNullOrEmpty(source.Name) ? source.Name : string.Empty;
            string sourceDescription = !string.IsNullOrEmpty(source.Description) ? source.Description : string.Empty;
           

            // Carregando configuração
            logFilePath = LoadConfiguration(system, logData, config, out errorLoggingConfiguration);

            // Validando parâmetros de entrada
            ValidateParameters(systemName, systemDescription, sourceName, sourceDescription, message, logData, errorLoggingConfiguration);

            if (extensionData != null && extensionData.ContainsKey("IsLogData") && extensionData["IsLogData"] is bool)
                isLogData = (bool)extensionData["IsLogData"];

            if (!isLogData)
            {
                // Capturando dados do ambiente
                EnvironmentCollector environmentCollector = CreateEnvironmentCollector(sourceName, logData, errorLoggingConfiguration);
                EnvironmentData environmentData = environmentCollector.Collect(extensionData);

                logData.System = system;
                logData.Source = source;
                logData.Type = type;
                logData.Date = DateTime.Now;

                logData.Message = message ?? "-";

                logData.Enviroment = environmentData;

                if (extensionData != null &&
                    extensionData.ContainsKey("InnerLogData") &&
                    !string.IsNullOrEmpty(extensionData["InnerLogData"] as string))
                {
                    logData.InnerLogData = (string)extensionData["InnerLogData"];
                }
            }

            TraceEventType traceEventType;
            EventLogEntryType eventLogEntryType;
            System.Windows.Forms.MessageBoxIcon messageBoxIcon;
            string systemSourceDescription = GetSystemSourceDescription(systemName, systemDescription, sourceName, sourceDescription);

            switch (type)
            {
                case LogType.Information:
                    traceEventType = TraceEventType.Information;
                    eventLogEntryType = EventLogEntryType.Information;
                    messageBoxIcon = System.Windows.Forms.MessageBoxIcon.Information;
                    break;

                case LogType.Warning:
                    traceEventType = TraceEventType.Warning;
                    eventLogEntryType = EventLogEntryType.Warning;
                    messageBoxIcon = System.Windows.Forms.MessageBoxIcon.Warning;
                    break;

                case LogType.Error:
                    traceEventType = TraceEventType.Error;
                    eventLogEntryType = EventLogEntryType.Error;
                    messageBoxIcon = System.Windows.Forms.MessageBoxIcon.Error;
                    break;

                default:
                    traceEventType = TraceEventType.Error;
                    eventLogEntryType = EventLogEntryType.Error;
                    messageBoxIcon = System.Windows.Forms.MessageBoxIcon.Error;
                    break;
            }


            bool xmlWritten = false;
            bool eventLogWritten = false;


            if ((outputType & LogOutputType.XmlFile) == LogOutputType.XmlFile)
            {
                if (!string.IsNullOrEmpty(logFilePath))
                {
                    // Cria a pasta de log ou apaga os arquivos antigos
                    logFilePath = CheckLogFiles(logFilePath, logData, errorLoggingConfiguration);

                    // Tenta gravar no xml
                    if (!string.IsNullOrEmpty(logFilePath))
                    {
                        try
                        {
                            string fileName = string.Format(logFilePath, DateTime.Now.ToString("yyyy-MM-dd"));

                            using (XmlWriterTraceListener xmlWriterTraceListener = new XmlWriterTraceListener(fileName))
                            {
                                XDocument document = XDocument.Parse(!isLogData ? logData.ToXml() : (string)message);
                                xmlWriterTraceListener.TraceData(null, systemSourceDescription, traceEventType, -1, document.CreateNavigator());
                                xmlWriterTraceListener.Flush();
                            }

                            xmlWritten = true;
                        }
                        catch (Exception ex)
                        {
                            if (logData.WriteErrors == null)
                                logData.WriteErrors = new List<string>();

                            logData.WriteErrors.Add(new Exception("Error writing to XmlWriterTraceListener", ex).ToString());
                        }
                    }
                }
            }
            else
            {
                xmlWritten = false;
            }

            if ((outputType & LogOutputType.EventViewer) == LogOutputType.EventViewer)
            {
                // Tenta gravar no event log
                try
                {
                    WriteEventEntry(EVENT_VIEWER_LOG_NAME, systemSourceDescription, !isLogData ? logData.ToString() : (string)message, eventLogEntryType);

                    eventLogWritten = true;
                }
                catch (Exception ex)
                {
                    if (logData.WriteErrors == null)
                        logData.WriteErrors = new List<string>();

                    logData.WriteErrors.Add(new Exception("Error writing to EventLog", ex).ToString());
                }
            }
            else
            {
                eventLogWritten = false;
            }

            // Se não conseguir gravar nos dois, exibe message box
            if (!xmlWritten && !eventLogWritten)
            {
                System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;
                System.Windows.Forms.MessageBox.Show(logData.ToString(), type.ToString(), System.Windows.Forms.MessageBoxButtons.OK, messageBoxIcon);
            }

            return logData;
        }
        #endregion

        #region Métodos Privados
        private static string LoadConfiguration(SystemComponent system, LogData logData, ErrorLoggingConfigurationSection config, out ErrorLoggingConfigurationSection errorLoggingConfiguration)
        {
            string logFilePath = null;

            // Tenta ler configuração
            try
            {
                if (config == null)
                {
                    ApplicationConfiguration applicationConfiguration = ApplicationConfiguration.GetCurrent(system);
                    errorLoggingConfiguration = applicationConfiguration.GetSection<ErrorLoggingConfigurationSection>("errorLoggingConfiguration");
                }
                else
                {
                    errorLoggingConfiguration = config;
                }

                // Captura caminho do arquivo de log
                logFilePath = errorLoggingConfiguration.GetLogFileName(system);
            }
            catch (Exception ex)
            {
                if (logData.ConfigurationErrors == null)
                    logData.ConfigurationErrors = new List<string>();

                // Se não conseguir ler a configuração, usa configuração default
                errorLoggingConfiguration = null;
                logFilePath = Path.Combine(string.Format(@"\{0}_{1}", system.Name, ErrorLoggingConfigurationSection.DefaultLogFilePath), ErrorLoggingConfigurationSection.LogFileName);

                logData.ConfigurationErrors.Add(new Exception(string.Format("Error reading configuration file, using default configuration. LogFilePath: '{0}'", Path.GetDirectoryName(logFilePath)), ex).ToString());
            }

            return logFilePath;
        }

        private static void ValidateParameters(string systemName,
                                               string systemDescription,
                                               string sourceName,
                                               string sourceDescription,
                                               object message,
                                               LogData logData,
                                               ErrorLoggingConfigurationSection errorLoggingConfiguration)
        {
            if (string.IsNullOrEmpty(systemName))
            {
                if (logData.ParameterErrors == null)
                    logData.ParameterErrors = new List<string>();

                logData.ParameterErrors.Add(new ArgumentNullException("system.Name", "The parameter system name cannot be null").ToString());
            }
            if (string.IsNullOrEmpty(systemDescription))
            {
                if (logData.ParameterErrors == null)
                    logData.ParameterErrors = new List<string>();

                logData.ParameterErrors.Add(new ArgumentNullException("system.Description", "The parameter system description cannot be null").ToString());
            }
            if (string.IsNullOrEmpty(sourceName))
            {
                if (logData.ParameterErrors == null)
                    logData.ParameterErrors = new List<string>();

                logData.ParameterErrors.Add(new ArgumentNullException("source.Name", "The parameter source name cannot be null").ToString());
            }
            if (string.IsNullOrEmpty(sourceDescription))
            {
                if (logData.ParameterErrors == null)
                    logData.ParameterErrors = new List<string>();

                logData.ParameterErrors.Add(new ArgumentNullException("source.Description", "The parameter source description cannot be null").ToString());
            }

            if (errorLoggingConfiguration != null && !errorLoggingConfiguration.Sources.Contains(sourceName))
            {
                if (logData.ParameterErrors == null)
                    logData.ParameterErrors = new List<string>();

                logData.ParameterErrors.Add(new ArgumentException(string.Format("Cannot find the parameter source '{0}' in configuration", sourceName), "source").ToString());
            }

            if (message == null)
            {
                if (logData.ParameterErrors == null)
                    logData.ParameterErrors = new List<string>();

                logData.ParameterErrors.Add(new ArgumentNullException("message", "The parameter message cannot be null").ToString());
            }
        }

        private static EnvironmentCollector CreateEnvironmentCollector(string source, LogData logData, ErrorLoggingConfigurationSection errorLoggingConfiguration)
        {
            if (errorLoggingConfiguration != null)
            {
                try
                {
                    LogSourceConfigurationElement config = errorLoggingConfiguration.Sources.Get(source);

                    if (config != null)
                    {
                        return Activator.CreateInstance(config.EnvironmentCollector) as EnvironmentCollector;
                    }
                    else
                    {
                        if (logData.ConfigurationErrors == null)
                            logData.ConfigurationErrors = new List<string>();

                        logData.ConfigurationErrors.Add(new Exception(string.Format("Environment collector not found for source '{0}', using default collector", source)).ToString());
                    }
                }
                catch (Exception ex)
                {
                    if (logData.ConfigurationErrors == null)
                        logData.ConfigurationErrors = new List<string>();

                    logData.ConfigurationErrors.Add(new Exception(string.Format("Error creating environment collector for source '{0}', using default collector", source), ex).ToString());
                }
            }

            return new EnvironmentCollector();
        }

        private static string GetSystemSourceDescription(string systemName,
                                                         string systemDescription,
                                                         string sourceName,
                                                         string sourceDescription)
        {
            if (string.IsNullOrEmpty(systemDescription))
                systemDescription = systemName;
            if (string.IsNullOrEmpty(sourceDescription))
                sourceDescription = sourceName;

           if (!string.IsNullOrEmpty(systemDescription) && !string.IsNullOrEmpty(sourceDescription))
                return string.Format("{0} - {1}", systemDescription, sourceDescription);
            else if (!string.IsNullOrEmpty(systemDescription))
                return systemDescription;
            else if (!string.IsNullOrEmpty(sourceDescription))
                return string.Format("{0} - {1}", EVENT_VIEWER_DEFAULT_SYSTEM, sourceDescription);

            return EVENT_VIEWER_DEFAULT_SYSTEM;
        }

        private static string CheckLogFiles(string logFilePath, LogData logData, ErrorLoggingConfigurationSection errorLoggingConfiguration)
        {
            bool logPathExists = false;

            // Primeiro verifica se a pasta de logs existe
            try
            {
                logPathExists = Directory.Exists(Path.GetDirectoryName(logFilePath));
            }
            catch (Exception ex)
            {
                if (logData.ConfigurationErrors == null)
                    logData.ConfigurationErrors = new List<string>();

                logData.ConfigurationErrors.Add(new Exception(string.Format("Error checking if log path '{0}' exists, using only EventLog", logFilePath), ex).ToString());

                logFilePath = null;
            }


            if (!string.IsNullOrEmpty(logFilePath))
            {
                if (logPathExists)
                {
                    // Se não ocorrerem erros e a pasta existir, apaga arquivos antigos
                    try
                    {
                        int daysToKeepLogFile = errorLoggingConfiguration != null ? errorLoggingConfiguration.DaysToKeepLogFile : ErrorLoggingConfigurationSection.DefaultDaysToKeepLogFile;

                        foreach (string file in Directory.GetFiles(Path.GetDirectoryName(logFilePath), "*Log_*.svclog"))
                        {
                            DateTime fileDate = File.GetCreationTime(file);

                            if (fileDate.AddDays(daysToKeepLogFile).Date <= DateTime.Now.Date)
                            {
                                File.Delete(file);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        if (logData.ConfigurationErrors == null)
                            logData.ConfigurationErrors = new List<string>();

                        logData.ConfigurationErrors.Add(new Exception("Error deleting old log files", ex).ToString());
                    }
                }
                else
                {
                    // Cria a pasta se ela não existir
                    try
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
                    }
                    catch (Exception ex)
                    {
                        if (logData.ConfigurationErrors == null)
                            logData.ConfigurationErrors = new List<string>();

                        logData.ConfigurationErrors.Add(new Exception(string.Format("Error creating log path '{0}', using only EventLog", logFilePath), ex).ToString());

                        logFilePath = null;
                    }
                }
            }

            return logFilePath;
        }

        private static void WriteEventEntry(string logName, string source, string message, EventLogEntryType eventLogEntryType)
        {
            if (!EventLog.Exists(logName, System.Environment.MachineName))
            {
                if (EventLog.SourceExists(source, System.Environment.MachineName))
                    EventLog.DeleteEventSource(source, System.Environment.MachineName);

                EventLog.CreateEventSource(source, logName, System.Environment.MachineName);
                EventLog currentEvent = GetEventLogByName(logName);

                currentEvent.MaximumKilobytes = EVENT_VIEWER_MAXIMUM_KILOBYTES;
                currentEvent.ModifyOverflowPolicy(OverflowAction.OverwriteAsNeeded, EVENT_VIEWER_RETENTION_DAYS);
            }

            if (!EventLog.SourceExists(source, System.Environment.MachineName))
                EventLog.CreateEventSource(source, logName, System.Environment.MachineName);

            string logSource = EventLog.LogNameFromSourceName(source, System.Environment.MachineName);
            if (!logSource.Equals(logName))
                throw new InvalidOperationException(string.Format("Invalid logsource name {0}. Expected {1}", logSource, logName));

            EventLog @event = GetEventLogByName(logName);

            @event.Source = source;
            @event.WriteEntry(message, eventLogEntryType);
        }

        private static EventLog GetEventLogByName(string logName)
        {
            EventLog[] eventLogs = EventLog.GetEventLogs(System.Environment.MachineName);
            EventLog @event = eventLogs.FirstOrDefault(el => el.Log.Equals(logName, StringComparison.InvariantCultureIgnoreCase));

            return @event;
        }

        //private static string Serialize(LogData logData)
        //{
        //    string msg;

        //    try
        //    {
        //        System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(LogData), new Type[] { logData.Enviroment.GetType() });

        //        using (StringWriter writer = new StringWriter())
        //        {
        //            serializer.Serialize(writer, logData);
        //            msg = writer.ToString();
        //        }
        //    }
        //    catch (Exception ex)
        //    {
        //        msg = logData.ToString(ex);
        //    }

        //    return msg;
        //}
        #endregion
    }
}
